from django.conf                      import settings
from django.core.exceptions           import ImproperlyConfigured
from django.core.urlresolvers         import reverse
from django.http                      import HttpResponse,HttpResponseRedirect
from django.http                      import HttpResponseServerError
from django.http                      import HttpResponsePermanentRedirect,HttpResponseForbidden
from django.template                  import loader,RequestContext
from django.utils.http                import urlquote
from django.views.decorators          import cache
from django.views.decorators          import csrf
from django.views.decorators          import http
from sitepal.sitebase.conf            import properties
from sitepal.sitemenu.actions.actions import *

class BaseView(object):
    def __init__(self,extra_context={},context_processors=None,template_loader=None,
                 template_name=None,template_object_name='object',mimetype=None,
                 url_args={},apply_to_args=None,cache_page=None,
                 csrf_protected=False,http_methods=('GET','POST'),login_required=False,
                 secured='optional',permissions={}):
        assert isinstance(template_object_name,basestring)
        assert isinstance(url_args,dict)
        assert isinstance(extra_context,dict) or callable(extra_context)
        assert secured in ('optional','required')
        assert isinstance(permissions,dict)
        self._template_loader      = template_loader or loader
        self._template_name        = template_name
        self._extra_context        = extra_context
        self._context_processors   = context_processors
        self._template_object_name = template_object_name
        self._mimetype             = mimetype
        self._url_args             = url_args
        self._apply_to_args        = apply_to_args
        self._login_required       = bool(login_required)
        self._secured              = secured
        self._action_permissions   = None
        if cache_page == 'never':
            self.handle_view = cache.never_cache(self.handle_view)
        elif isinstance(cache_page,(int,long)):
            self.handle_view = cache.cache_page(cache_page)(self.handle_view)
        elif cache_page is not None:
            raise ValueError,"Cache page must be either the amount of seconds, 'never' or None"
        if csrf_protected:
            self.__call__ = csrf.csrf_protect(self.__call__)
        if http_methods == 'GET':
            self.__call__ = http.require_GET(self.__call__)
        elif http_methods == 'POST':
            self.__call__ = http.require_POST(self.__call__)
        elif isinstance(http_methods,(tuple,list)):
            self.__call__ = http.require_http_methods(http_methods)(self.__call__)
        else:
            raise ValueError,"HTTP methods must be either a string or a list of method names"
        if ACTION_READ_TEMPLATE not in permissions:
            permissions[ACTION_READ_TEMPLATE] = "sites.read_template"
        for action,perms in permissions.iteritems():
            if isinstance(perms,basestring):
                self.add_perms(action,perms)
            else:
                self.add_perms(action,*perms)

    def get_template(self):
        if not hasattr(self,'_template_cache'):
            if self._template_name:
                self._template_cache = self._template_loader.get_template(self._template_name)
            else:
                self._template_cache = u''
        return self._template_cache
    template = property(get_template)

    def response(self,request,context):
        if not self.has_perm(ACTION_READ_TEMPLATE,request.user):
            return HttpResponseForbidden()
        return HttpResponse(self.template.render(context),mimetype=self._mimetype)

    def add_perms(self,action,*perms):
        if not self._action_permissions:
            self._action_permissions = {action: perms}
        else:
            self._action_permissions[action] = perms

    def has_perm(self,action,user,obj=None):
        if not self._action_permissions:
            return False
        if action in self._action_permissions:
            perm_list = self._action_permissions[action]
            if isinstance(perm_list,basestring):
                return user.has_perm(perm_list,obj)
            for perm_set in perm_list:
                if isinstance(perm_set,basestring):
                   granted = user.has_perm(perm_set,obj) 
                else:
                   granted = user.has_perms(perm_set,obj) 
                if granted:
                    return True
        return False

    def apply_extra_context(self,request,context,template_obj,kwargs={}):
        for key,value in kwargs.iteritems():
            if callable(value):
                context[key] = value(request,context,template_obj)
            else:
                context[key] = value
        if callable(self._extra_context):
            extra = self._extra_context(request,context,template_obj)
        else:
            extra = self._extra_context
        for key,value in extra.iteritems():
            if callable(value):
                context[key] = value(request,context,template_obj)
            else:
                context[key] = value

    def get_context(self,request,template_obj,**kwargs):
        context = RequestContext(request,
                                 {self._template_object_name: template_obj},
                                  self._context_processors)
        if hasattr(request,'menu_view') and 'title' not in kwargs:
            kwargs['title'] = request.menu_view.title
        self.apply_extra_context(request,context,template_obj,kwargs)
        return context

    def __call__(self,request,*args,**kwargs):
        from sitepal.sitemenu.actions import Http403
        try:
            if getattr(settings,'HTTPS_SUPPORT',False):
                if self._secured == 'forbidden' and request.is_secure():
                    request_url  = request.build_absolute_uri(request.get_full_path())
                    redirect_url = request_url.replace('https://','http://')
                    if settings.DEBUG:
                        print "Redirect to: {0}".format(redirect_url)
                    else:
                        return HttpResponsePermanentRedirect(redirect_url)
                if self._secured == 'required' and not request.is_secure():
                    request_url  = request.build_absolute_uri(request.get_full_path())
                    redirect_url = request_url.replace('http://','https://')
                    if settings.DEBUG:
                        print "Redirect to: {0}".format(redirect_url)
                    else:
                        return HttpResponsePermanentRedirect(redirect_url)
            if self._login_required and not request.user.is_authenticated():
                return self.redirect_login(request)
            if self._apply_to_args:
                self._apply_to_args(request,args,kwargs)
            if request.method == 'GET':
                return self.handle_get(request,*args,**kwargs)
            elif request.method == 'POST':
                return self.handle_post(request,*args,**kwargs)
            return HttpResponseServerError()
        except Http403:
            return HttpResponseForbidden()

    def handle_get(self,request):
        context = self.get_context(request,None)
        return self.response(request,context)

    def handle_post(self,request):
        return HttpResponseServerError()

    def redirect_login(self,request):
        path         = urlquote(request.get_full_path())
        field        = properties.get('user-login-redirect-field',admin=True)
        redirect_url = '{0}?{1}={2}'.format(reverse('user_login'),field,path)
        return HttpResponseRedirect(redirect_url)

    def redirect(self,redirect_to,request,obj):
        if isinstance(redirect_to,basestring):
            return HttpResponseRedirect(redirect_to.format(obj.__dict__))
        elif callable(redirect_to):
            return HttpResponseRedirect(redirect_to(request,obj))
        elif hasattr(obj,'get_absolute_url'):
            return HttpResponseRedirect(obj.get_absolute_url())
        else:
            raise ImproperlyConfigured("No URL to redirect to.")

